package com.ledi.guns.config.web;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.ledi.guns.config.properties.GunsProperties;
import com.ledi.guns.core.shiro.CustomizedModularRealmAuthenticator;
import com.ledi.guns.core.shiro.ShiroAlipayAuthRealm;
import com.ledi.guns.core.shiro.ShiroDbRealm;
import com.ledi.guns.core.shiro.ShiroKit;

/**
 * shiro权限管理的配置
 *
 * @author fengshuonan
 * @date 2016年11月14日 下午3:03:44
 */
@Configuration
public class ShiroConfig {

	/**
	 * 安全管理器
	 */
	@Bean
	public DefaultWebSecurityManager securityManager(CookieRememberMeManager rememberMeManager,
			CacheManager cacheShiroManager, SessionManager sessionManager) {

		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealms(realms());
		securityManager.setCacheManager(cacheShiroManager);
		securityManager.setRememberMeManager(rememberMeManager);
		securityManager.setSessionManager(sessionManager);
		securityManager.setAuthenticator(authenticator());
		return securityManager;
	}

	/**
	 * spring session管理器（多机环境）
	 */
	@Bean
	@ConditionalOnProperty(prefix = "guns", name = "spring-session-open", havingValue = "false")
	public ServletContainerSessionManager servletContainerSessionManager() {

		return new ServletContainerSessionManager();
	}

	/**
	 * session管理器(单机环境)
	 */
	@Bean
	@ConditionalOnProperty(prefix = "guns", name = "spring-session-open", havingValue = "true")
	public DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager,
			GunsProperties gunsProperties) {

		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setCacheManager(cacheShiroManager);
		sessionManager.setSessionValidationInterval(gunsProperties.getSessionValidationInterval() * 1000);
		sessionManager.setGlobalSessionTimeout(gunsProperties.getSessionInvalidateTime() * 1000);
		sessionManager.setDeleteInvalidSessions(true);
		sessionManager.setSessionValidationSchedulerEnabled(true);
		Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
		cookie.setName("shiroCookie");
		cookie.setHttpOnly(true);
		sessionManager.setSessionIdCookie(cookie);
		return sessionManager;
	}

	/**
	 * 缓存管理器 使用Ehcache实现
	 */
	@Bean
	public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {

		EhCacheManager ehCacheManager = new EhCacheManager();
		ehCacheManager.setCacheManager(ehcache.getObject());
		return ehCacheManager;
	}

	/**
	 * 项目自定义的Realm
	 */
	@Bean
	public ShiroDbRealm shiroDbRealm() {

		HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher();
		md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.hashAlgorithmName);
		md5CredentialsMatcher.setHashIterations(ShiroKit.hashIterations);
		ShiroDbRealm shiroDbRealm = new ShiroDbRealm();
		shiroDbRealm.setCredentialsMatcher(md5CredentialsMatcher);
		return shiroDbRealm;
	}

	@Bean
	public ShiroAlipayAuthRealm shiroAlipayOathRealm() {

		ShiroAlipayAuthRealm shiroAlipayAuthRealm = new ShiroAlipayAuthRealm();
		shiroAlipayAuthRealm.setCredentialsMatcher(new SimpleCredentialsMatcher());
		return shiroAlipayAuthRealm;
	}

	private List<Realm> realms() {

		List<Realm> realms = new ArrayList<Realm>();
		realms.add(shiroDbRealm());
		realms.add(shiroAlipayOathRealm());
		return realms;
	}

	/**
	 * rememberMe管理器, cipherKey生成见{@code Base64Test.java}
	 */
	@Bean
	public CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {

		CookieRememberMeManager manager = new CookieRememberMeManager();
		manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));
		manager.setCookie(rememberMeCookie);
		return manager;
	}

	/**
	 * 记住密码Cookie
	 */
	@Bean
	public SimpleCookie rememberMeCookie() {

		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setMaxAge(7 * 24 * 60 * 60);// 7天
		return simpleCookie;
	}

	/**
	 * Shiro的过滤器链
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {

		ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
		shiroFilter.setSecurityManager(securityManager);
		/**
		 * 默认的登陆访问url
		 */
		shiroFilter.setLoginUrl("/restApi/login");
		/**
		 * 登陆成功后跳转的url
		 */
		shiroFilter.setSuccessUrl("/");
		/**
		 * 没有权限跳转的url
		 */
		shiroFilter.setUnauthorizedUrl("/global/error");
		/**
		 * 配置shiro拦截器链 anon 不需要认证 authc 需要认证 user 验证通过或RememberMe登录的都可以
		 * 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的
		 * 顺序从上到下,优先级依次降低
		 */
		Map<String, String> hashMap = new LinkedHashMap<>();
		hashMap.put("/**/restApi/login", "anon");
		hashMap.put("/**/restApi/logout", "anon");
		hashMap.put("/**/restApi/user/**", "anon");
		hashMap.put("/static/**", "anon");
		hashMap.put("/login", "anon");
		hashMap.put("/restApi/notify/**", "anon");
		hashMap.put("/global/sessionError", "anon");
		hashMap.put("/kaptcha", "anon");
		hashMap.put("/restApi/scan", "anon");
		hashMap.put("/restApi/h5Apply", "anon");
		hashMap.put("/restApi/file", "anon");
		hashMap.put("/restApi/kaptcha", "anon");
		hashMap.put("/trigger/**", "anon");
		// hashMap.put("/**", "user");

		shiroFilter.setFilterChainDefinitionMap(hashMap);
		return shiroFilter;
	}

	/**
	 * 在方法中 注入 securityManager,进行代理控制
	 */
	@Bean
	public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {

		MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
		bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
		bean.setArguments(new Object[] { securityManager });
		return bean;
	}

	/**
	 * Shiro生命周期处理器: 用于在实现了Initializable接口的Shiro
	 * bean初始化时调用Initializable接口回调(例如:UserRealm) 在实现了Destroyable接口的Shiro
	 * bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)
	 */
	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {

		return new LifecycleBeanPostProcessor();
	}

	/**
	 * 启用shrio授权注解拦截方式，AOP式方法级权限检查
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
			DefaultWebSecurityManager securityManager) {

		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	@Bean
	public CustomizedModularRealmAuthenticator authenticator() {

		CustomizedModularRealmAuthenticator customerAuthenticator = new CustomizedModularRealmAuthenticator();
		customerAuthenticator.setRealms(realms());
		return customerAuthenticator;
	}

}
